﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Cpuss.Strategies.Properties;

namespace Cpuss.Strategies
{

    /// <summary>
    /// Shortest-Job-First strategy.
    /// </summary>
    /// <remarks>
    /// This is a priority strategy. The smaller the burst time a process has left to run, the higher
    /// it's priority is.
    /// </remarks>
    public sealed class ShortestJobFirst : IStrategy
    {

        private readonly int _pollTime;

        /// <summary>
        /// Creates and intializes a new instance of the SJF strategy using a specified poll time.
        /// </summary>
        /// <param name="pollTime">Poll time.</param>
        /// <exception cref="ArgumentOutOfRangeException"><strong>pollTime</strong> is less than <strong>1</strong>.</exception>
        public ShortestJobFirst(int pollTime)
        {
            if (pollTime < 1)
            {
                throw new ArgumentOutOfRangeException(Resources.PollTimeGreaterThanZero);
            }

            _pollTime = pollTime;
        }

        /// <summary>
        /// Executes the processes in <see cref="Runner.ProcessLoad"/> in shortest job first order.
        /// </summary>
        /// <param name="runner">Runner to use as execution enviroment.</param>
        public void Execute(Runner runner)
        {
            List<Process> readyQueue = new List<Process>(runner.ProcessLoad);
            while (readyQueue.Count > 0)
            {
                try
                {
                    // select the process that is eligible to run with the smallest remaining burst time
                    Process p = readyQueue.Where(x => x.ArrivalTime <= runner.Time).OrderBy(x => x.BurstTime - x.Data.UtilizedCpuTime).First();
                    runner.UtilizeCpu(p, _pollTime);
                    if (Process.IsComplete(p))
                    {
                        runner.LogProcessMetrics(p);
                        readyQueue.Remove(p);
                    }
                }
                catch (InvalidOperationException)
                {
                    runner.SkipIdleCpuTime();
                }
            }
        }

        /// <summary>
        /// Returns a <see cref="string"/> that represents the current <see cref="object"/>.
        /// </summary>
        /// <returns>String representation of object.</returns>
        public override string ToString()
        {
            return string.Format(CultureInfo.InvariantCulture, "Shortest Job First (Poll Time = {0})", _pollTime);
        }

    }
}
